#include "ads1299_driver.h"

#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/task.h"
#include "driver/gpio.h"

static const char *TAG = "m_debug";


/* gpio pins and spi pins */

#define SPI_CS_0 GPIO_NUM_27 // Pin 16, according to esp32-pico-d4_datasheet
#define SPI_CS_1 GPIO_NUM_26 // Pin 15
#define SPI_CS_2 GPIO_NUM_15 // Pin 21
#define SPI_CS_3 GPIO_NUM_33 // Pin 13        

#define ADS_DRV_START_PIN GPIO_NUM_25 // Pin 14
#define GPIO_OUTPUT_PIN_SEL ((1ULL << SPI_CS_0) | (1ULL << SPI_CS_1) | (1ULL << SPI_CS_2) | (1ULL << SPI_CS_3) | (1ULL << ADS_DRV_START_PIN))
#define ADS_DRDY_PIN GPIO_NUM_32 // Pin 12

#define PIN_NUM_MISO 12 // pin 18
#define PIN_NUM_MOSI 13 // pin 20
#define PIN_NUM_CLK 14  // pin 17

#define test_pin GPIO_NUM_16 // Pin 25       


#define ESP_INTR_FLAG_DEFAULT 0

spi_device_handle_t spi;

/* - - - - - - - - - - - - - - - -*/

ads1299_drv_handler_t ads1299_drv_handler;
uint8_t dummy_u8 = 0;
uint16_t dummy_len = 1;

uint16_t log_counter = 0;
static void IRAM_ATTR drdy_gpio_isr_handler(void *arg)
{    
    // do not do the polling spi here, this function should be quick
    log_counter++;
    if(log_counter>=100){
        //led_toggle();
        log_counter = 0;
        // ESP_LOGI(TAG, " gpio_config_ads_drv ");

        // gpio_set_level(test_pin,(1-gpio_get_level(test_pin)));
    }

    ads1299_drv_handler(&dummy_u8,dummy_len);
  
}

static void gpio_config_ads_drv(void)
{
    gpio_config_t io_conf = {
        .pin_bit_mask = GPIO_OUTPUT_PIN_SEL,
        .mode = GPIO_MODE_INPUT_OUTPUT,
        .intr_type = GPIO_INTR_DISABLE,
        .pull_down_en = 0,
        .pull_up_en = true};
    gpio_config(&io_conf);

    // DRDY pins:
    gpio_config_t io_conf_2 = {
        .pin_bit_mask = BIT64(ADS_DRDY_PIN),
        .mode = GPIO_MODE_INPUT,
        // for powersave reasons, the GPIO should not be floating, use pullup
        .pull_up_en = true,
        .pull_down_en = false,
        .intr_type = GPIO_INTR_NEGEDGE,
    };
    gpio_config(&io_conf_2);

    gpio_set_level(SPI_CS_0, 1);
    gpio_set_level(SPI_CS_1, 1);
    gpio_set_level(SPI_CS_2, 1);
    gpio_set_level(SPI_CS_3, 1);

    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(ADS_DRDY_PIN, drdy_gpio_isr_handler, (void *)ADS_DRDY_PIN);

    // ESP_LOGI(TAG, " gpio_config_ads_drv ");
}

static void spi_config_ads_drv(void)
{
    esp_err_t ret;
    //    spi_device_handle_t handle;

    // Configuration for the SPI bus
    spi_bus_config_t buscfg;
    memset(&buscfg, 0, sizeof(spi_bus_config_t));
    buscfg.mosi_io_num = PIN_NUM_MOSI;
    buscfg.miso_io_num = PIN_NUM_MISO;
    buscfg.sclk_io_num = PIN_NUM_CLK;

    // Configuration for the SPI device on the other side of the bus
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 4000000, // 4M 
        // Note that I use spi speed of 4M, which is slow enough so in the ads1299 driver I do not need to worry about the tSDECODE problem.
        .mode = 1,
        .spics_io_num = -1,
        .queue_size = 4,
        // .command_bits = 0,
        // .address_bits = 0,
    };

    // Initialize the SPI bus
     ret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
    // ret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_DISABLED);
    ESP_ERROR_CHECK(ret);

    ret = spi_bus_add_device(SPI2_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);

    // ESP_LOGI(TAG, " spi_config_ads_drv ");
}

int hardware_config(void)
{
    gpio_config_ads_drv();
    spi_config_ads_drv();
    return 0;
}

void delay_ads_drv(uint32_t period_ms)
{
    vTaskDelay(period_ms / portTICK_PERIOD_MS);
    // ESP_LOGI(TAG, " period_ms %ld ",period_ms);
}

void pin_high(int i)
{
    gpio_set_level(i, 1);
    // ESP_LOGI(TAG, " cs_high ");
}

void pin_low(int i)
{
    gpio_set_level(i, 0);
    // ESP_LOGI(TAG, " cs_low ");
}

#define Dummy_Byte 0x00

/*This should be a polling transmit*/
int spi_send_one_byte(uint8_t u)
{
    esp_err_t ret;

    uint8_t data[3];
    data[0] = u;

    spi_transaction_t spi_t;
    memset(&spi_t, 0, sizeof(spi_t)); // Zero out the transaction
    spi_t.length = 8;
    spi_t.tx_buffer = data;

    ret = spi_device_polling_transmit(spi, &spi_t);
    assert(ret == ESP_OK);

    ESP_LOGI(TAG, " spi_send_one_byte %x ",u);
    return ret;
}

/*This should be a polling transmit*/
int spi_send_recv(uint8_t const *p_tx_buffer, uint8_t *p_rx_buffer, uint16_t len)
{
    esp_err_t ret;

    spi_transaction_t spi_t;
    memset(&spi_t, 0, sizeof(spi_t)); // Zero out the transaction
    spi_t.length = len * 8;
    spi_t.tx_buffer = p_tx_buffer;
    spi_t.rx_buffer = p_rx_buffer;

    ret = spi_device_polling_transmit(spi, &spi_t);
    assert(ret == ESP_OK);
    // ESP_LOGI(TAG, " spi_send_recv %x  %x  %x ",p_tx_buffer[0],p_tx_buffer[1],p_tx_buffer[2]);
    // ESP_LOGI(TAG, " rx : %x  %x  %x ",p_rx_buffer[0],p_rx_buffer[1],p_rx_buffer[2]);
    return ret;
}

ads1299_t m_ads_arr[4] = {0};

void ads1299_dev_init(ads1299_drv_t m_drv){
    ads1299_drv_handler = m_drv.callback_handler;
    m_ads_arr[0].hardware_config = hardware_config;// init the spi and gpio

    for (int i = 0; i < CHIP_NUM; i++)
    {
        m_ads_arr[i].pin_low = pin_low;
        m_ads_arr[i].pin_high = pin_high;
        m_ads_arr[i].delay_ms = delay_ads_drv;
        m_ads_arr[i].xfer_one_byte = spi_send_one_byte;
        m_ads_arr[i].xfer = spi_send_recv;
        m_ads_arr[i].START_PIN = ADS_DRV_START_PIN;
    }

    // set to -1 if not USE
    // m_ads_arr[0].CS_PIN = PIN_NOT_USE;
    m_ads_arr[0].CS_PIN = SPI_CS_0;
    m_ads_arr[1].CS_PIN = SPI_CS_1;
    m_ads_arr[2].CS_PIN = SPI_CS_2;
    m_ads_arr[3].CS_PIN = PIN_NOT_USE;

    m_ads_arr[0].BIAS_BUFFER = 1;
    m_ads_arr[1].BIAS_BUFFER = 0;
    m_ads_arr[2].BIAS_BUFFER = 0;
    m_ads_arr[3].BIAS_BUFFER = 0;   

    ads1299_init_hardware(&m_ads_arr[0]);// just do onece
    for (int i = 0; i < CHIP_NUM; i++)
    {
        ads1299_setup(&m_ads_arr[i]);
    }    
    ads_start_by_start_pin(&m_ads_arr[0]);// start the first will be fine, because all START pins wired together 
}

void ads1299_drv_check_id(void){
    for (int i = 0; i < CHIP_NUM; i++){
        ads1299_get_id(&m_ads_arr[i]);
        ESP_LOGI(TAG, "  revId : %d ", m_ads_arr[i].id.revId); // should be 1
        ESP_LOGI(TAG, "  devId : %d ", m_ads_arr[i].id.devId); //shold be 3 
        ESP_LOGI(TAG, "  nuCh : %d ", m_ads_arr[i].id.nuCh); // 2:8channels, 1:6channels, 0:4channels
    }
}

// make sure the rx buff longer than 27*CHIP_NUM bytes
// use this in RDATAC mode
void ads1299_drv_get_data(uint8_t * rx){
    for (int i = 0; i < CHIP_NUM; i++){
        ads1299_get_data(&m_ads_arr[i],&rx[i*DATA_PACKAGE_LEN]);
    }        
}
